package com.ztr.suanfa.aes;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;


public class AesGood {

    /**
     * AES 对称加密（RSA非对称加密）
     * CBC 有向量 （ECB 无向量）
     * PKCS5Padding 填充模式（NoPadding 无填充）
     */

    // 这个常量定义了一个AES加密算法的字符串表示，其中CBC是密码块链模式（Cipher Block Chaining），而PKCS5Padding是填充方式。
    // 这意味着当你使用这个算法进行加密时，数据会被分为块，并使用CBC模式进行加密，同时使用PKCS5的方式进行填充以确保数据块的大小是合适的。
    private static final String ALG_AES_CBC_PKCS5 = "AES/CBC/PKCS5Padding";

    // 这个常量简单地表示了AES算法的名字。
    private static final String ALGORITHM = "AES";

    // 这是一个字符集常量，表示UTF-8编码。
    private static final Charset UTF8 = StandardCharsets.UTF_8;

    // 指定好的秘钥，非Base64和16进制
    private String aesKey = "12e476beac1a4g20";

    // 偏移量
    private String aesIv = "2e119e58a526bc64";

    private SecretKeySpec skeySpec;

    private IvParameterSpec iv;


    /**
     * 加密
     *
     * @param plainText 明文
     * @return Base64编码的密文
     * @throws Exception 加密异常
     */
    public String encrypt(String plainText) throws Exception {
        Cipher cipher = Cipher.getInstance(ALG_AES_CBC_PKCS5);
        skeySpec = new SecretKeySpec(aesKey.getBytes(), ALGORITHM);
        iv = new IvParameterSpec(aesIv.getBytes());
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
        // 这里的编码格式需要与解密编码一致
        byte[] encryptText = cipher.doFinal(plainText.getBytes(UTF8));
        return Base64.encodeBase64String(encryptText);
    }

    /**
     * 解密方法
     *
     * @param cipherStr Base64编码的加密字符串
     * @return 解密后的字符串(UTF8编码)
     * @throws Exception 异常
     */
    public String decrypt(String cipherStr) throws Exception {
        // step 1 获得一个密码器
        Cipher cipher = Cipher.getInstance(ALG_AES_CBC_PKCS5);
        // step 2 初始化密码器，指定是加密还是解密(Cipher.DECRYPT_MODE 解密; Cipher.ENCRYPT_MODE 加密)
        // 加密时使用的盐来够造秘钥对象
        skeySpec = new SecretKeySpec(aesKey.getBytes(), ALGORITHM);
        // 加密时使用的向量，16位字符串
        iv = new IvParameterSpec(aesIv.getBytes());
        cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
        // 对加密报文进行base64解码
        byte[] encrypted1 = Base64.decodeBase64(cipherStr);
        // 解密后的报文数组
        byte[] original = cipher.doFinal(encrypted1);
        // 输出utf8编码的字符串，输出字符串需要指定编码格式
        return new String(original, UTF8);
    }

    public static void main(String[] args) {
        AesGood testOne = new AesGood();
        String plainText = "z123456789";
        String cipherStr;
        try {
            System.out.println("被加解密的报文:[ " + plainText + " ]");
            cipherStr = testOne.encrypt(plainText);
            System.out.println("AES 加密后的Base64报文:[ " + cipherStr + "]");
            System.out.println("对加密后的报文解密后的明文为:[ " + testOne.decrypt(cipherStr) + " ]");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


